import tornado.web
import json
import requests
import collections
import traindata
import sys
import time
import re
from urllib import parse
from HttpService import IsTest
# {
#  "environmental_label":"美式^1,20平^1,主卧^1" ,     -- 当前环境标签
#  "type":"combination" ,
#  "item_list":[{"IDentifierType":"shafa",
#                "gmodel_id":"31292",
#                "cat_sub":"沙发",
#                "q_info":" goods_length:[1800 TO 4500] AND goods_width:[1000 TO 2000]  "
# 			    },
# 				{"IDentifierType":"chaji",
#                "gmodel_id":"30721",
#                "cat_sub":"茶几",
#                "q_info":"goods_length:[600 TO 2000] AND goods_width:[300 TO 800] "
# 			    }
#  ]
# }
testString = "{\"space\":\"\u5ba2\u5385\",\"environmental_label\":\"\u7f8e\u5f0f^1\",\"type\":\"combination\",\"item_list\":[{\"IDentifierType\":\"shafa\",\"gmodel_id\":\"31292\",\"cat_sub\":\"\u6c99\u53d1\",\"q_info\":\"\"},{\"IDentifierType\":\"ditan\",\"gmodel_id\":\"2255\",\"cat_sub\":\"\u5730\u6bef\",\"q_info\":\"\"},{\"IDentifierType\":\"dianshigui\",\"gmodel_id\":\"30726\",\"cat_sub\":\"\u7535\u89c6\u67dc\",\"q_info\":\"\"},{\"IDentifierType\":\"shafa\",\"gmodel_id\":\"5322\",\"cat_sub\":\"\u6c99\u53d1\",\"q_info\":\"\"},{\"IDentifierType\":\"chaji\",\"gmodel_id\":\"30721\",\"cat_sub\":\"\u8336\u51e0\",\"q_info\":\"\"},{\"IDentifierType\":\"chaji\",\"gmodel_id\":\"30721\",\"cat_sub\":\"\u8336\u51e0\",\"q_info\":\"\"}]}"
environKey="environment_tag_words"
useInputKey="input_tag_words"
inputEnvironLabelKey = "environmental_label"
modelWeightKey = "relevancy_score"
modelTagKey = "tag_words_new"
modelTypeName = "cat_sub"
modelControllerKey = "match_attr"
extraSklearn = "sklearn"
spareKey = "alternate_key"
modelAllCount = "30"
host = "http://58.220.17.162:28181"#"http://58.220.17.162:8281/DSORM/service/querySolr.do"
formalHost = "http://solr.wx.com:8181"
if IsTest:
    pass
else:
    host = formalHost

def StringValid (val):
    return val and type(val) == type("") and val != ""

class Query:
    def __init__(self, content, MFinalFind, cache):
        self.content = content
        self.MFinalFind = MFinalFind
        self.cache = cache
        self.contentJson = json.loads(self.content)

        if "method" in self.contentJson and self.contentJson["method"] == "sklearn":
            self.sklearn = True
        else:
            self.sklearn = False

        if self.sklearn:
            return

        self.analysis_space()
        self.analysis_attrs()
        self.analysis_items()
        self.analysis_type()
        self.analysis_casetype()
        self.analysis_cache()
        self.analysis_limitnum()

        self.setParentModel(None)
        self.setParent(None)
        self.setItem(self.items)
        self.setResult({
            "stop":False,
            "cnt": 0,
            "maxcnt": 50,
            "result": [],
            "process": []
        })
        self.process = []

    def analysis_space(self):
        self.space = ""
        key = "space"
        if key not in self.contentJson:
            return
        val = self.contentJson.get(key)
        if not StringValid(val):
            return
        self.space = val

    def analysis_attrs(self):
        global inputEnvironLabelKey
        self.attrs = {}
        self.attrLabel = ""

        key = inputEnvironLabelKey
        if key not in self.contentJson:
            return
        val = self.contentJson.get (key)
        self.attrLabel = val
        if not StringValid (val):
            return
        valArray = val.split (",")
        for _val in valArray:
            _valArray = _val.split ("^")

            _name,_weight = "", 1

            if len(_valArray) != 0:
               _name = _valArray [0]
            if len(_valArray) > 1:
                try:
                    _tempweight = _valArray [1]
                    _weight = int(_tempweight)
                except:
                    pass

            if not StringValid(_name):
                continue
            _name = _name.strip()
            if _weight <= 0:
                _weight = 1

            if _name in self.attrs:
                _weight += self.attrs.get(_name)
            self.attrs [_name] = _weight

    def analysis_type(self):
        self.type = ""
        key = "type"
        if key not in self.contentJson:
            return
        self.type = self.contentJson.get(key)

    def analysis_items(self):
        self.items=[]
        self.item=None
        key = "item_list"
        if key not in self.contentJson:
            return
        _tmp = self.contentJson.get(key)
        if _tmp and type(_tmp) == type([]):
            self.items = _tmp

    def analysis_casetype(self):
        key = 'case_type'
        self.case_type = 0
        if key not in self.contentJson:
            return
        ctype = self.contentJson.get(key)
        try:
            self.case_type=int(ctype)
        except:
            pass
    def analysis_cache(self):
        key = "nocache"
        self.execCache = True
        if key not in self.contentJson:
            return
        val = self.contentJson.get(key, 0)
        self.execCache = val != 1

    def get_casetype(self):
        key = 'case_type'
        try:
            val = self.item.get(key, self.case_type)
            val = int(val)
        except:
            val = self.case_type
        return val

    def analysis_limitnum(self):
        key = 'limit_num'
        self.limit_num = 1
        if key not in self.contentJson:
            return
        lnum = self.contentJson.get(key)
        try:
            self.limit_num = int(lnum)
        except:
            pass

    def finalattrs(self):
        global modelTagKey
        finalattr = {}
        for _ak,_av in self.attrs.items():
            finalattr[_ak] = _av

        def _tohandle(_val):
            nonlocal finalattr
            if _val is None or _val == "":
                return
            _valArray = _val.split(',')
            for _v in _valArray:
                _varray = _v.split('^')
                try:
                    _vk = _varray[0].strip()
                    if _vk == "":
                        continue
                    _vv = 1
                    if len(_varray) > 1:
                        _vv = int(_varray[1])
                    if _vk in finalattr:
                        _vv += finalattr[_vk]
                    finalattr [_vk] = _vv
                except:
                    pass

        def _handle(model):
            nonlocal finalattr
            if model is None:
                return
            if modelTagKey not in model:
                return
            _val = model.get (modelTagKey)
            _tohandle (_val)

        self.recursionallmodel(_handle)

        if 'attr' in self.itemExtraReqParam:
            attrval = self.itemExtraReqParam['attr']
            _tohandle (attrval)

        return finalattr

    def queryreq(self):
        global modelTypeName, modelTagKey
        req = {"method":"get"}
        skey,akey,ikey = "space","attr","item"
        nkey,nval = "numoftag", 15
        dkey,dval = "dropdup", 3

        req[skey] = self.space;

        aval = ""
        for _ak,_av in self.finalattrs().items():
            if aval:
                aval += ","
            aval +=  "%s^%s" % (_ak,_av)

        req[akey] = aval

        ival = ""
        try:
            ival = self.item[modelTypeName]
        except:
            ival = ""

        req[ikey] = ival

        req[nkey] = nval
        req[dkey] = dval
        calKey,orKey = "calratio", "or"
        req[calKey] = 100
        req[orKey] = ""

        for ck,cv in self.itemExtraReqParam.items():
            if ck == 'attr':
                continue
            if ck not in req or ck in [nkey,dkey,calKey,orKey]:
                req[ck] = cv
            else:
                rv = req[ck]
                if rv:
                    rv += ","
                rv += cv
                req[ck] = rv

        return req

    def query(self, queryapi):
        if self.result['cnt'] >= self.result['maxcnt'] or self.result["stop"] == True:
            return

        self.result['cnt'] += 1
        self.queryapi = queryapi

        if self.item.get("retain", -1) != -1:
            models = self.item ["retain"]
        else:
            begintime = time.time()
            req = self.queryreq()
            reqString = json.dumps(req, ensure_ascii=False)
            self.process.append(reqString)
            resultJson = self.queryapi (reqString)
            print("frequency", time.time()-begintime)

            attrString = self.item.get("alternate_key", "")
            if attrString is not None and attrString != "":
                for value in resultJson.values():
                    if len(value) != 0:
                        continue
                    for k in attrString.split(','):
                        val = k.split('^')
                        k = val [0]
                        try:
                            v = int(val[1])
                        except:
                            v = 1
                        if k in value:
                            v += value[k]
                        value[k]=v
            self.process.append(json.dumps(resultJson, ensure_ascii=False))
            begintime=time.time()
            models = self.datafromsolr(resultJson)
            print("request",time.time()-begintime)
            if int(self.item.get("is_retain", 0)) == 1:
                self.item["retain"] = models
        self.parsenext(models)

        return self.result

    def mquery(self, queryapi):
        if self.sklearn:
            content = self.contentJson ["content"]
            case_type = int(self.contentJson ['case_type'])

            if self.MFinalFind.__code__.co_argcount == 2:
                w, p, no = self.MFinalFind(content, case_type)
            else:
                w, p, no = self.MFinalFind(content)

            noarray = []
            for n in no:
                noarray.append(str(n))
            return {"w":str(w),"p":str(p), "a": noarray}
        else:
            self.query(queryapi)
            return self.formatResult()

    def formatResult(self):
        dic = {}
        result = []

        fields = ['old_sculpt_goods_id', 'old_sculpt_gmodel_id', 'gmodel_id', 'old_gmodel_id', 'old_goods_id', 'goods_id', 'id', 'IDentifierType', 'scuplt_IDentifierType', 'is_retain']
        for item in self.result["result"]:
            _result = {
                "objs": [],
                "attr": ""
            }
            _rets,_attrs = _result ["objs"],{}
            objs = item ["objs"]
            if objs:
                for obj in objs:
                    _ret = {}
                    for field in fields:
                        if field in obj:
                            _ret[field] = obj.get(field)
                    _rets.append(_ret)
                    try:
                        objattrString = obj['tag_words_new']
                        objattrs = objattrString.split(',')
                        for _attr in objattrs:
                            if _attr is None or _attr == "":
                                continue
                            if _attr in _attrs:
                                _attrs[_attr]+=1
                            else:
                                _attrs[_attr]=1
                    except:
                        pass

            for _attr, _val in _attrs.items():
                if _result["attr"]:
                    _result["attr"] += ","
                _result["attr"] += _attr + "^" + str(_val)

            if 'debug' in self.contentJson and self.contentJson['debug'] == "1":
                _result ['process'] = item['process']
            result.append(_result)

        if len(self.result["process"]) != 0:
            dic["process"] = self.result["process"]
        dic["result"] = result
        return dic

    def recursionallmodel(self,action):
        if action is None:
            return
        self.recursionallmodelanditem(lambda x,y:action(x))

    def recursionallmodelanditem(self,action):
        temp = self
        while temp is not None:
            model = temp.parentModel
            if action:
                action(model, None if temp.parent is None else temp.parent.item)
            temp = temp.parent

    def paramofsolr(self):
        template = "if(map(query({!dismax qf=%s v=%s}),0.00001,100000,1,0),1,0)"
        keys = ['group_id', 'suit_id', 'group_suit_id']
        rets = [[],[],[]]

        def _hanle(model):
            nonlocal rets, keys
            if model is None:
                return
            i = 0
            for key in keys:
                if key in model:
                    val = model [key]
                    if val is not None and val != "" and val and val != "0":
                        ret = rets[i]
                        if val not in ret:
                            ret.append(val)
                i+=1

        self.recursionallmodel(_hanle)

        result,vals = [],[]
        i = 0
        for key in keys:
            ret = rets[i]
            if len(ret) != 0:
                val = ','.join(ret)
                vals.append(val)
                val = template % (key, val)
                result.append(val)
            else:
                result.append("")
                vals.append("*")
            i+=1
        return result,keys,vals

    def dynamicQParam(self):
        qinfo = self.item.get("dynamic_q_info", "")
        if qinfo is None or qinfo == "":
            return ""
        params = re.findall("\$.*?\$", qinfo)
        if params is None or len(params) == 0:
            return ""
        paramDict = {}
        for par in params:
            if par.find("|") == -1:
                continue
            pkeys = par.split("|")
            pk = pkeys[0].replace("$", "")
            pv = pkeys[1].replace("$", "")
            paramDict[pk] = pv

        NotReplace = True
        def handle (_cur,_item):
            nonlocal params,qinfo,NotReplace
            if _cur and _item:
                fg = str(_item.get("input_stack", 0))
                if fg != "1":
                    return
                for k,v in _cur.items():
                    if k not in paramDict or v is None or v == "":
                        continue
                    key = "${0}|{1}$".format(k, paramDict[k])
                    val = "{0}{1}{2}".format(v,paramDict[k],key)
                    qinfo = qinfo.replace(key, val)
                    NotReplace = False

        self.recursionallmodelanditem(handle)
        if NotReplace == True:
            return ""
        else:
            for k,v in paramDict.items():
                par = "${0}|{1}$".format(k,v)
                ipar = v+par
                qinfo = qinfo.replace(ipar, "")
                qinfo = qinfo.replace(par, "")
            return qinfo

    def datafromsolr(self, resultJson):
        global environKey,modelAllCount,host,useInputKey
        action = traindata.solrActionVersion1()
        sortarray,sortkeys,sortvals = self.paramofsolr()
        sort = collections.OrderedDict()
        sortindex = -1
        for val in sortarray:
            sortindex+=1
            sortkey = "$"+sortkeys[sortindex]
            if not val or val == "":
                pass
            else:
                sort[val] = "desc"
            sortval = sortvals[sortindex]
            if action.find(sortkey) != -1:
                action = action.replace(sortkey, sortval)
        temp = ""
        for k,v in sort.items():
            tmp = k + " " + v
            if len(temp) != 0:
                temp+=","
            temp+=tmp
        if len(temp) != 0:
            temp+=","
        action = action % (temp)
        dynamicQ = self.dynamicQParam ()
        action = action.replace("$dynamic_q_info", dynamicQ)
        key = "$tags_wors"
        val = ""
        if self.item is not None:
            for _key,_val in self.item.items():
                k = "$"+_key
                if action.find(k) == -1:
                    continue
                action = action.replace(k, _val)
        envval = self.attrLabel
        if self.item.get("use_tag", "").find(environKey) == -1:
            envval=""
        inputuseval = self.item.get(useInputKey, "")
        if self.item.get("use_tag", "").find(useInputKey) == -1:
            inputuseval = ""
        if (envval is None or envval == "") and (useInputKey is None or useInputKey == ""):
            envval = self.attrLabel
        if action.find("$" + environKey) != -1:
            action = action.replace("$" + environKey, envval)
        if action.find("$" + useInputKey) != -1:
            action = action.replace("$" + useInputKey, inputuseval)
        for _key,_val in resultJson.items():
            for _k,_v in _val.items():
                if val:
                    val += " "
                val += "%s^%s" % (_k,_v)
        if action.find(key) != -1:
            action = action.replace(key,val)
        key = "$start"
        val = "0"
        action = action.replace(key,val)
        key = "$rows"
        val = modelAllCount
        action = action.replace(key,val)
        self.process.append(action)
        URL = host + action
        URLArray = URL.split("?")
        URLArray[1] = re.sub("\s+", " ", URLArray[1])
        URL = URLArray[0] + "?" + parse.quote(URLArray[1], safe="^,=&;!*:$(){}")
        ret = self.request(URL)
        return ret

    def requestsolr(self, URL):
        ret = []
        try:
            begintime = time.time()
            res = requests.get (URL,timeout=10)
            if res.ok:
                if self.cache is not None:
                    self.cache.cache(URL, res.text)
                resObj = json.loads(res.text)
                ret = resObj["response"]["docs"]
                print("solr", time.time()-begintime)
            else:
                self.process.append("response:error")
            res.close()
        except Exception as e:
            print(e)
            if res is not None:
                res.close()
            ret.append(self.item)
        return ret

    def request(self, URL):
        if self.cache is not None and self.cache.exist(URL) and self.execCache:
            value = self.cache.value(URL)
            resObj = json.loads(value)
            ret = resObj["response"]["docs"]
        else:
            ret = self.requestsolr(URL)
        if type(ret) != type([]):
            ret = []
        if len(ret) == 0:
            ret.append (self.item)

        return ret

    def parsesklearn(self):
        global extraSklearn
        if extraSklearn not in self.item:
            return []
        val = self.item.get(extraSklearn, "");
        if len(val) == 0:
            return []
        ret =[]
        for tmp in val.split('&'):
            try:
                results = tmp.split(":")
                type = results[0]
                attrs = []
                for attr in results[1].split(","):
                    try:
                        attrs.append(attr.split("^")[0])
                    except:
                        pass
                ret.append({type:attrs})
            except:
                pass
        return ret

    def sortmodels(self, models):
        global modelTypeName, modelTagKey,modelWeightKey
        hrets = []
        def _handle(model):
            if model is None:
                return
            typename, tags = "", []
            if modelTypeName in model:
                typename = model.get(modelTypeName, typename)
            if modelTagKey in model:
                try:
                    _tag = model.get(modelTagKey)
                    tags += _tag.split(',')
                except Exception as e:
                    print(e)
            if typename and typename != "" and len(tags) != 0:
                hrets.append({typename:tags})
        self.recursionallmodel(_handle)
        hprets = hrets
        hrets = []
        smodels = []
        if models and len(models) != 0:
            for model in models:
                _handle (model)
                if len(hrets) == 0:
                    continue
                tmp = []+hprets+hrets+self.parsesklearn()
                hrets.clear()
                if self.MFinalFind is None:
                    w = 60
                    p = 0
                else:
                    if self.MFinalFind.__code__.co_argcount == 2:
                        w,p,no = self.MFinalFind(tmp,self.get_casetype())
                    else:
                        w,p,no = self.MFinalFind(tmp)
                r = 0
                try:
                    r = float(model[modelWeightKey])
                except:
                    r = 0
                s = 0
                if "is_series" in model:
                    _s = model.get("is_series")
                    try:
                        s = int(_s)
                    except:
                        s = 0
                try:
                    if self.contentJson.get("debug", "0") == "1":
                        model['tmp'] = json.dumps(tmp, ensure_ascii=False)
                        model['no'] = json.dumps(no)
                except:
                    pass
                model['s'] = s
                model['w'] = w
                model['p'] = p if w > 5 else 1 - p
                model['r'] = r #if w > 5 else 100 - r
                smodels.append(model)
        defaultSorted,sortedString = "s,w,p,r", "sorted"
        sortedValue = self.contentJson.get (sortedString, defaultSorted)
        sortedValue = self.item.get (sortedString, sortedValue)
        if sortedValue == "":
            sortedValue = defaultSorted

        def lambdakey (m):
            nonlocal sortedValue
            lambdaval = ()
            for sval in sortedValue.split(','):
                if sval not in m:
                    continue
                lambdaval += (m[sval],)
            return lambdaval

        smodels = sorted(smodels, key=lambdakey, reverse=True)
        for m in smodels:
            if m['w'] > 5:
                continue
            m['p'] = 1 - m['p']
            #m['r'] = 100 - m['r']
        return smodels

    def parsenext(self, models):
        global modelTypeName
        if models and len(models) != 0:
            oldkeys = ["gmodel_id", "goods_id", "sculpt_gmodel_id", "sculpt_goods_id"]
            retainattrs = [modelTypeName, "IDentifierType", "scuplt_IDentifierType", 'is_retain']
            for model in models:
                for oldkey in oldkeys:
                    if oldkey in self.item:
                        model['old_' + oldkey] = self.item[oldkey]
                for ra in retainattrs:
                    if ra in self.item:
                        model[ra]=self.item[ra]

            if len(models) > 1:
                begintime = time.time()
                models = self.sortmodels(models)
                print("sklearn", time.time()-begintime)
            if 'debug' in self.contentJson:
                for m in models:
                    _m,_ka=collections.OrderedDict(),["s","w","p","r","no","tmp","goods_id","gmodel_id","tag_words_new"]
                    for _kav in _ka:
                        if _kav not in m:
                            continue
                        mv = m.get(_kav)
                        _m[_kav] = str(mv)
                    self.process.append(_m)
            for model in models:
                if self.result['stop'] == True:
                    break
                if self.next:
                    child = Query(self.content, self.MFinalFind, self.cache)
                    child.setResult(self.result)
                    child.setParentModel(model)
                    child.setParent(self)
                    child.setItem(self.newItems())
                    child.query(self.queryapi)
                else:
                    self.parseResult(model)

    def tryGetWeightWithModel(self, model):
        global modelWeightKey
        w = 0.0
        try:
            w = float(model[modelWeightKey])
        except:
            w = 0.0
        return w

    def parseResult(self, model):
        rets,_process = [], []
        weight = 0.0
        if model is not None:
            rets.insert(0, model)
            weight += self.tryGetWeightWithModel(model)
        temp = self
        while temp is not None:
            pmodel = temp.parentModel
            if pmodel:
                rets.insert(0, pmodel)
                weight += self.tryGetWeightWithModel(pmodel)
            _process.insert(0, temp.process)
            temp = temp.parent

        ret = {}
        ret["weight"] = weight
        ret["objs"] = rets
        ret["process"] = _process

        self.result["result"].append(ret)
        self.result["stop"] = len(self.result["result"]) == self.limit_num

    def setParentModel(self, model):
        self.parentModel = model

    def setParent(self, parent):
        self.parent = parent

    def setItem(self, items):
        global modelControllerKey
        if len(items) != 0:
            self.item = items.pop(0)
        self.next = len(items) != 0
        self.items = items

        self.itemExtraReqParam = {}
        try:
            modelCVal = self.item.get(modelControllerKey, "")
            for cval in modelCVal.split("&"):
                try:
                    kv = cval.split("=")
                    if kv[0] == "" or kv[1] == "":
                        continue
                    self.itemExtraReqParam [kv[0]] = kv[1]
                except:
                    continue
        except:
            pass

    def newItems(self):
        _items = []
        for _item in self.items:
            _items.append(_item)
        return _items

    def setResult(self, obj):
        self.result = obj